package nachos.threads;

import nachos.machine.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;

/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 */
public class Alarm {
    /**
     * Allocate a new Alarm. Set the machine's timer interrupt handler to this
     * alarm's callback.
     *
     * <p><b>Note</b>: Nachos will not function correctly with more than one
     * alarm.
     */
    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });

	threads = new HashMap< KThread, Long >( );
    }

    /**
     * The timer interrupt handler. This is called by the machine's timer
     * periodically (approximately every 500 clock ticks). Causes the current
     * thread to yield, forcing a context switch if there is another thread
     * that should be run.
     */
    public void timerInterrupt() {

	// iterate over the list of time waiting threads and wake up any that
	// have passed there time
	boolean intStatus = Machine.interrupt().disable();

	Iterator< Map.Entry<KThread, Long> > iter = threads.entrySet().iterator();

	while ( iter.hasNext() )
	    {
		Map.Entry<KThread, Long > ent = iter.next();

		// if time has passed
		if ( ent.getValue() < Machine.timer().getTime() )
		    {
			// set to ready
			ent.getKey().ready();
			
			// remove from list
			// [Mike] I was getting a concurrent
			// modification exception here
			// occasionally. The solution is to use
			// iter.remove instead of removing the key
			// from the map directly while you're
			// iterating over it.
			// threads.remove( ent.getKey() );
			iter.remove();
		    } // end of if time has passed loop
	    } // end of iterator while loop

	Machine.interrupt().restore( intStatus ); // yield will disable interrupts, do i need to restore before this???

	// still need to yield the processor for preemption
	KThread.currentThread().yield();
    }

    /**
     * Put the current thread to sleep for at least <i>x</i> ticks,
     * waking it up in the timer interrupt handler. The thread must be
     * woken up (placed in the scheduler ready set) during the first timer
     * interrupt where
     *
     * <p><blockquote>
     * (current time) >= (WaitUntil called time)+(x)
     * </blockquote>
     *
     * @param	x	the minimum number of clock ticks to wait.
     *
     * @see	nachos.machine.Timer#getTime()
     */
    public void waitUntil(long x) {
	// for now, cheat just to get something working (busy waiting is bad)
	long wakeTime = Machine.timer().getTime() + x;
	//while (wakeTime > Machine.timer().getTime())
	//    KThread.yield();
	
	// really want to sleep the thread - so need a way to store threads waiting to 
	// wake up
	boolean intStatus = Machine.interrupt().disable();
	threads.put( KThread.currentThread(), wakeTime );
	
	KThread.sleep();
	Machine.interrupt().restore( intStatus );
    }

    // added to block the threads instead of busy wait
    // Tom - I am not crazy about this implementation because wakeTime can
    // be the same for different threads and there is no multimap as part 
    // of the core java. the other idea is to use a sorted list.
    private HashMap< KThread, Long > threads;
    
}
